// #![allow(unused)]
#![allow(unused_variables)]
#![allow(dead_code)]

use std::env;
use std::fmt::Formatter;
use std::string::ToString;
use std::sync::Arc;
use std::time::Duration;

use axum::{Extension, Router};
use axum::http::{header, StatusCode};
use axum::routing::{get, get_service, post};
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
use tower::ServiceBuilder;
use tower_http::compression::CompressionLayer;
use tower_http::cors::CorsLayer;
use tower_http::sensitive_headers::SetSensitiveHeadersLayer;
use tower_http::services::ServeDir;
use tower_http::timeout::TimeoutLayer;
use tower_http::trace;
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer};

use crate::auth::AuthSession;
use crate::auth::jwt::JwtBackend;
use crate::auth::middleware::AuthManagerLayer;
use crate::auth::permission::{FilterMode, PermissionType};
use crate::user::{Claims, login, RestAuthEndpoint};

mod app_resp;
mod user;
mod app_req;
mod auth;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    if let Err(_) = dotenvy::dotenv() {
        println!("No .env file found, using default values");
    }
    tracing_subscriber::fmt::init();
    let host_var = env::var("HOST");
    let host = host_var.as_deref().unwrap_or("0.0.0.0");
    let port_var = env::var("PORT");
    let port = port_var.as_deref().unwrap_or("8000");
    let jwt_secret_str = env::var("JWT_SECRET").expect("JWT_SECRET is not set in .env file");
    let jwt_secret = jwt_secret_str.as_bytes();
    let server_url = format!("{host}:{port}");

    // let conn = init_db().await;
    // let state = AppState { conn };
    let validation = {
        let mut validation = jsonwebtoken::Validation::new(jsonwebtoken::Algorithm::HS256);
        validation.set_audience(&["APP"]);
        validation
    };
    let state = AppState {
        jwt_info: Arc::new(JwtInfo {
            encoding_key: jsonwebtoken::EncodingKey::from_secret(jwt_secret),
            validation: validation.clone(),
            header: jsonwebtoken::Header::new(jsonwebtoken::Algorithm::HS256),
        })
    };

    let auth_layer =
        AuthManagerLayer::new(
            FilterMode::default()
                .with_default_mode(PermissionType::Authenticated)
                .add_route("/login", PermissionType::Anonymous)
                .add_route("/408", PermissionType::Anonymous)
                .add_route("/", PermissionType::AllPermission(
                    ["C".to_string(), "B".to_string()].into_iter().collect()
                ))
            ,
            JwtBackend::<Claims>::new(header::AUTHORIZATION, jwt_secret, validation),
            RestAuthEndpoint,
        );

    let app = Router::new()
        .route("/", get(|user: Extension<AuthSession<JwtBackend<Claims>>>| async move {
            let username = user.user.as_ref().map(|user| user.sub.as_str()).unwrap_or("");
            format!("Hello, World! {}", username)
        }))
        .route("/login", post(login).get(|| async { "qwqq" }))
        .route("/408", get(|| async { StatusCode::REQUEST_TIMEOUT }))
        .layer(
            ServiceBuilder::new()
                .layer(
                    trace::TraceLayer::new_for_http()
                        .on_request(trace::DefaultOnRequest::new().level(tracing::Level::INFO))
                        .on_response(trace::DefaultOnResponse::new().level(tracing::Level::INFO))
                )
                .layer(auth_layer)
                .layer(SessionManagerLayer::new(MemoryStore::default())
                    .with_secure(false)
                    .with_expiry(Expiry::OnSessionEnd))
                .layer(CorsLayer::permissive())
                .layer(SetSensitiveHeadersLayer::new(std::iter::once(
                    header::AUTHORIZATION,
                )))
                .layer(CompressionLayer::new())
                .layer(TimeoutLayer::new(Duration::from_secs(10)))
                .into_inner()
        )
        .fallback_service(
            get_service(ServeDir::new(concat!(env!("CARGO_MANIFEST_DIR"), "/static")))
                .handle_error(|error| async move {
                    (
                        StatusCode::INTERNAL_SERVER_ERROR,
                        format!("Unhandled internal error: {error}"),
                    )
                }),
        )
        .with_state(state);
    let listener = tokio::net::TcpListener::bind(&server_url).await.expect("web服务启动绑定地址异常");
    axum::serve(listener, app).await?;
    Ok(())
}

async fn init_db() -> DatabaseConnection {
    let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file");
    let mut con_opt = ConnectOptions::new(db_url);
    con_opt.max_connections(3)
        .min_connections(0)
        .connect_timeout(Duration::from_secs(6));
    Database::connect(con_opt).await.expect("Database connection failed")
}

#[derive(Clone, Debug)]
pub struct AppState {
    // conn: DatabaseConnection,
    pub jwt_info: Arc<JwtInfo>,
}

#[derive(Clone)]
pub struct JwtInfo {
    pub encoding_key: jsonwebtoken::EncodingKey,
    pub validation: jsonwebtoken::Validation,
    pub header: jsonwebtoken::Header,
}

impl std::fmt::Debug for JwtInfo {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("JwtInfo")
            .field("encoding_key", &&"xxx" as &dyn std::fmt::Debug)
            .field("validation", &self.validation)
            .field("header", &self.header)
            .finish()
    }
}